// 7.5 构造函数再探
/**
 * 7.5.4 隐式的类类型转换
 * 如果构造函数只接受一个实参，则它实际上定义了转换为此类类型的隐式转换机制，
 * 有时我们把这种构造函数称作转换构造函数（converting constructor）。
 * 能通过一个实参调用的构造函数定义了一条从构造函数的参数类型向类类型隐式转换的规则。
 */

#include <iostream>
#include <vector>
#include "Sales_data.h"
//#include "Screen.h" // 难道是因为Window_mgr被声明成Screen的友元的原因？导致这个源文件同时包含这两个头文件时会导致一些问题？
#include "WindowMgr.h"
using std::cerr, std::clog;
using std::cin, std::cout, std::endl;

int main()
{
    Sales_data item; // 默认构造的item对象
    string null_book = "9-999-99999-9";
    // 构造一个临时的Sales_data对象
    // 该对象的units_sold和revenue等于0，bookNo等于null_book
    item.combine(null_book); // combine函数的参数是const Sales_data&，这里使用null_book作为实参传递，实际上发生了隐式的类类型转换

    // 只允许一步类类型转换
    // 错误，这是因为进行了两步转换，先把"9-999-99999-9"转成了string，然后再把这个临时的string转成了Sales_data
    item.combine("9-999-99999-9"); // no suitable constructor exists to convert from "const char [14]" to "Sales_data"
    // 正确，显示的转换成string，隐式地转换成Sales_data
    item.combine(string("9-999-99999-9"));
    // 正确，隐式地转换成string，显示地转换成Sales_data
    item.combine(Sales_data("9-999-99999-9"));

    // 抑制构造函数定义的隐式转换
    // 使用explicit阻止构造函数的隐式转换（但explicit的构造函数可以使用直接初始化）
    item.combine(null_book); // 一旦将构造函数用explicit修饰则无法进行隐式转换，如：explicit Sales_data(const std::string &s) : bookNo(s) {}

    // 关键字explicit只对一个实参的构造函数有效。
    // 需要多个实参的构造函数不能用于执行隐式转换，所以无须将这些构造函数指定为explicit的。
    // 只能在类内声明构造函数时使用explicit关键字，在类外部定义时不应重复


    // explicit构造函数只能用于直接初始化
    Sales_data item2 = null_book; // 错误，不能将explicit构造函数用于拷贝形式初始化过程
    Sales_data item3(null_book); // 正确，直接初始化

    // 为转换显示地使用构造函数
    // 尽管编译器不会将explicit的构造函数用于隐式转换过程，但是我们可以使用这样的构造函数显式地强制进行转换：
    item.combine(Sales_data(null_book)); // 正确，实参是个显示构造的Sales_data对象
    item.combine(static_cast<Sales_data>(cin)); // 正确，static_cast可以使用explicit的构造函数

    return 0;
}